﻿using System;
using System.Collections.Generic;
using System.Text;
using OpenMCPL.MediaCenter.Code.Controls;
using Microsoft.MediaCenter;
using Microsoft.MediaCenter.Hosting;
using System.Reflection;

namespace OpenMCPL.MediaCenter.Code
{
    public class VirtualApplication : BaseModelItem, IAddInModule, IAddInEntryPoint
    {
        private AddInHost host;
        private HistoryOrientedPageSessionEx session;

        static readonly VirtualApplication instance = new VirtualApplication();

        static VirtualApplication()
        {
        }

        VirtualApplication()
        {
        }

        //private static VirtualApplication(HistoryOrientedPageSessionEx session, AddInHost host)
        //{
        //    //set our location to the programdata dir - if we don't theme support breaks!
        //    //Environment.CurrentDirectory = OMLEngine.FileSystemWalker.PublicRootDirectory;
        //    //need to start checking ehres themes on startup!
        //    //ThemeManager.CreateThemeFiles();
        //}

        public virtual void Initialize(Dictionary<string, object> appInfo, Dictionary<string, object> entryPointInfo)
        {
        }

        public virtual void Uninitialize()
        {
        }

        public void Launch(AddInHost host)
        {
            AssemblyResolver.MakeCustomAssembliesWellknown();

            instance.session = new HistoryOrientedPageSessionEx(instance);
            instance.host = host;
            //AddInHost.Current
            //session.GoToPageWithoutHistory("resx://OpenMCPL.MediaCenter/OpenMCPL.MediaCenter.Resources/Loader", new Dictionary<string, object>());
            //session.GoToPage("resx://OpenMCPL.MediaCenter.Example/OpenMCPL.MediaCenter.Example.Resources/Default");
            //Microsoft.MediaCenter.UI.Application.DeferredInvoke(new Microsoft.MediaCenter.UI.DeferredHandler(this.BeginStart), (object)host, new TimeSpan(100));
        }

        public static VirtualApplication Instance
        {
            get { return instance; }
        }

        public HistoryOrientedPageSessionEx Session
        {
            get { return session; }
        }

        public MediaCenterEnvironment MediaCenterEnvironment
        {
            get
            {
                if (host == null) return null;
                return host.MediaCenterEnvironment;
            }
        }
    }

    //public class Singleton<T> where T : class, new()
    //{

    //    private static object _syncobj = new object();
    //    private static volatile T _instance = null;
    //    public static T Instance
    //    {
    //        get
    //        {
    //            if (_instance == null)
    //            {
    //                lock (_syncobj)
    //                {
    //                    if (_instance == null)
    //                    {
    //                        _instance = new T();
    //                    }
    //                }
    //            }
    //            return _instance;
    //        }
    //    }

    //    public Singleton()
    //    { }

    //}

    //public class Foo : Singleton<Foo>
    //{
    //}

    public abstract class Allocator<T> : IDisposable
        where T : class
    {
        /// <summary>
        /// The parameterless protected Constructor.
        /// </summary>
        protected Allocator()
        { }

        /// <summary>
        /// The property returns the only instance of the Singleton Object in question.
        /// </summary>
        /// <remarks>This property implementation must enforce the Single Object property of Singletons throwing an exception.</remarks>
        public abstract T Instance { get; }

        /// <summary>
        /// The implementation of the IDisposable interface.
        /// </summary>
        public abstract void Dispose();
    }

    /// <summary>
    /// An static threadsafe allocator that creates the object on class loading by the framework.
    /// </summary>
    /// <remarks>The framework specifies that the static constructor is called in an isolated compartment so there is no posibility that multiple threads can access the static constructor.</remarks>
    public class StaticAllocator<T> : Allocator<T> where T : class
    {
        static StaticAllocator()
        {
            ConstructorInfo constructor = typeof(T).GetConstructor(BindingFlags.Instance | BindingFlags.NonPublic, null, new Type[0], new ParameterModifier[0]);
            if (constructor == null)
                throw new Exception("The object that you want to singleton doesnt have a private/protected constructor so the property cannot be enforced.");

            try
            {
                instance = constructor.Invoke(new object[0]) as T;
            }
            catch (Exception e)
            {
                throw new Exception("The StaticSingleton couldnt be constructed, check if the type T has a default constructor", e);
            }
        }

        private StaticAllocator()
        { }

        private static readonly T instance;

        /// <summary>
        /// The static allocator Instance property returns the instance created on class loading.
        /// </summary>
        /// <remarks>This means that the singleton is instantiated at the moment in which a class has a reference to that type even if it never calls the Instance method.</remarks>
        public override T Instance
        {
            get { return instance; }
        }

        public override void Dispose()
        { }
    }

    /// <summary>
    /// An StaticSingleton using an StaticAllocator used just to simplify the inheritance syntax.
    /// </summary>
    public class StaticSingleton<T> : Singleton<T, StaticAllocator<T>> where T : class
    { }

    public class Singleton<T, Allocator> : IDisposable
        where T : class
        where Allocator : Allocator<T>
    {
        #region Logger

        //private static readonly ILog log = LogManager.GetLogger(typeof(Singleton<T, Allocator>));

        #endregion

        private static readonly Allocator<T> allocator;
        /// <summary>
        /// The Singleton implementation of the Instance method defer the creation policy to its allocator, so this method just delegate the Instance retrieval to the Instance method of the allocator.
        /// </summary>
        public static T Instance
        {
            get { return allocator.Instance; }
        }

        /// <summary>
        /// The protected parameterless constructor used to not allow the creation of multiple Singleton classes. This property must be enforced by the singleton users.
        /// </summary>
        static Singleton()
        {
            ConstructorInfo constructor = typeof(Allocator).GetConstructor(BindingFlags.Instance | BindingFlags.NonPublic, null, new Type[0], new ParameterModifier[0]);
            if (constructor == null)
                throw new Exception((string)"The allocator that you want to create doesnt have a private/protected constructor.");

            try
            {
                allocator = constructor.Invoke(new object[0]) as Allocator<T>;
            }
            catch (Exception e)
            {
                throw new Exception("The Singleton Allocator couldnt be constructed, check if the type Allocator has a default constructor", e);
            }
        }

        /// <summary>
        /// The standard Dispose pattern.
        /// </summary>
        public virtual void Dispose()
        {
            allocator.Dispose();
            GC.SuppressFinalize(this);
        }
    }

//    public class Base<T> where T : class
//{
//    #region Instance
//    static T m_Instance;
//    public static T Instance
//    {
//        get { return m_Instance; }
//    }
//    #endregion
//    static Base()
//    {
//        m_Instance = Activator.CreateInstance(typeof(T), true) as T;
//    }
//    protected Base()
//    {
//    }
//}
//public class Derived : Base
//{
//    #region Name
//    private string m_Name;
//    public string Name
//    {
//        get { return m_Name; }
//        set { m_Name = value; }
//    }
//    #endregion

//    private Derived()
//    {
//    }
//}
}
